package com.link.gateway.core.utils;

import com.link.gateway.core.cllog.LCLogger;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class DateUtil {
    private static final Logger logger = LogManager.getLogger(DateUtil.class);

    private DateUtil() {
    }

    /**
     * yyyy-MM-dd HH:mm:ss
     */
    public static final String DATE_TIME = "yyyy-MM-dd HH:mm:ss";

    /**
     * yyyy-MM-dd
     */
    public static final String DAY = "yyyy-MM-dd";

    /**
     * 日期格式:精确到天
     */
    public static final SimpleDateFormat DAY_FORMATTER = new SimpleDateFormat(DAY);

    /**
     * 时间格式:精确到秒
     */
    public static final SimpleDateFormat DATETIME_FORMATTER = new SimpleDateFormat(DATE_TIME);


    public static final Pattern PATTERN = Pattern.compile("^[1-2][0-9]{3}-(0[1-9]|1[0-2])-([0-2][1-9]|3[0-1])$");

    /**
     * 根据传入的时间和格式化标准对日期进行字符串转换
     *
     * @param date         时间
     * @param formatterStr 格式化模版
     * @return
     */
    public static String dateToStr(Date date, String formatterStr) {
        if (StringUtils.isNull(formatterStr)) {
            formatterStr = DATE_TIME;
        }
        try {
            SimpleDateFormat format = new SimpleDateFormat(formatterStr);
            return format.format(date);
        } catch (Exception e) {
            LCLogger.withException("时间转换字符串错误：" + e.toString());
            return "0000-00-00 00:00:00";
        }
    }


    /**
     * 字符串转换为时间
     *
     * @param str          字符串
     * @param formatterStr 格式化模版
     * @return
     */
    public static Date stringToDate(String str, String formatterStr) {
        Date date = null;
        try {
            if (StringUtils.isNull(formatterStr)) {
                formatterStr = DATE_TIME;
            }
            SimpleDateFormat format = new SimpleDateFormat(formatterStr);
            date = format.parse(str);
            return date;
        } catch (ParseException e) {
            LCLogger.withException("字符串转换为时间错误：" + e.toString());
            return null;
        }
    }


    /**
     * 判断str是否为yyyy-mm-dd的时间格式，是怎返回true;否返回false
     *
     * @param str
     * @return
     */
    public static boolean isDayFormatString(String str) {
        Matcher matcher = PATTERN.matcher(str);
        return matcher.matches();
    }

    /**
     * Unix时间戳转换字符串
     * 返回格式：yyyy-MM-dd HH:mm:ss
     *
     * @return
     */
    public static String unixTimetampToDateString(String unixTimetamp) {
        Long timeStamp = Long.valueOf(unixTimetamp) * 1000;
        synchronized (DATETIME_FORMATTER) {
            // 时间戳转换成时间
            return DATETIME_FORMATTER.format(new Date(timeStamp));
        }
    }

    /**
     * 获取当前Long的时间值
     *
     * @return
     */
    public static Long getCurrentLongTime() {
        return System.currentTimeMillis();
    }

    /**
     * 根据系统时间，返回上周最后一天，周一算一周的第一天
     *
     * @return
     */
    public static String getLastWeekLastDay() {
        Calendar monday = Calendar.getInstance();
        monday.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
        monday.add(Calendar.DATE, -1);
        Date date = new Date(monday.getTimeInMillis());
        synchronized (DAY_FORMATTER) {
            return DAY_FORMATTER.format(date);
        }
    }

    /**
     * 根据系统时间，返回下周第一天，周一算一周的第一天
     *
     * @return
     */
    public static String getNextWeekFirstDay() {
        Calendar sunday = Calendar.getInstance();
        sunday.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
        sunday.add(Calendar.DATE, 7);
        Date date = new Date(sunday.getTimeInMillis());
        synchronized (DAY_FORMATTER) {
            return DAY_FORMATTER.format(date);
        }
    }


    public static int getFirstWeekDay(String str) {
        SimpleDateFormat formatYMD = new SimpleDateFormat(DAY);// formatYMD表示的是yyyy-MM-dd格式
        SimpleDateFormat formatD = new SimpleDateFormat("E");// "E"表示"day in week"
        Date d = null;
        String weekDay = "";
        int n = 0;
        try {
            d = formatYMD.parse(str);
            weekDay = formatD.format(d);
            if ("星期日".equals(weekDay)) {
                n = 6;
            } else if ("星期六".equals(weekDay)) {
                n = 5;
            } else if ("星期五".equals(weekDay)) {
                n = 4;
            } else if ("星期四".equals(weekDay)) {
                n = 3;
            } else if ("星期三".equals(weekDay)) {
                n = 2;
            } else if ("星期二".equals(weekDay)) {
                n = 1;
            } else if ("星期一".equals(weekDay)) {
                n = 0;
            }
        } catch (Exception e) {
            logger.error("DateUtil捕获异常: ", e);
        }
        return n;
    }

    /**
     * 获取传入月份的第一天
     *
     * @param date
     * @return
     */
    public static Date getMonthStart(Date date) {
        if (null == date) {
            date = new Date();
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        int index = calendar.get(Calendar.DAY_OF_MONTH);
        calendar.add(Calendar.DATE, (1 - index));
        return calendar.getTime();
    }

    /**
     * 获取传入月份的最后一天
     *
     * @param date
     * @return
     */
    public static Date getMonthEnd(Date date) {
        if (null == date) {
            date = new Date();
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.MONTH, 1);
        int index = calendar.get(Calendar.DAY_OF_MONTH);
        calendar.add(Calendar.DATE, (-index));
        return calendar.getTime();
    }


    /**
     * 根据传入的日期获取所在月份所有日期
     *
     * @param
     * @return
     */
    public static List<String> getAllDaysOfMonth(Date param) {
        List<String> list = new ArrayList();
        Date monthStart = getMonthStart(param);
        Date monthEnd = getMonthEnd(param);
        while (!monthStart.after(monthEnd)) {
            synchronized (DAY_FORMATTER) {
                list.add(DAY_FORMATTER.format(monthStart));
            }
            monthStart = getNext(monthStart);
        }
        return list;
    }


    private static Date getNext(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DATE, 1);
        return calendar.getTime();
    }


    /**
     * 给指定的时间添加year年
     *
     * @param date
     * @param year
     * @return
     */
    public static Date addYearToDate(Date date, int year) {
        if (date == null) {
            return date;
        }
        return addTimes(Calendar.YEAR, date, year);
    }

    /**
     * 给指定的时间添加month月
     *
     * @param date
     * @param month
     * @return
     */
    public static Date addMonthToDate(Date date, int month) {
        if (date == null) {
            return date;
        }
        return addTimes(Calendar.MONTH, date, month);
    }

    /**
     * 给指定的时间添加day天
     *
     * @param date
     * @param day
     * @return
     */
    public static Date addDayToDate(Date date, int day) {
        if (date == null) {
            return date;
        }
        return addTimes(Calendar.DATE, date, day);
    }

    /**
     * 给指定的时间添加hour小时
     *
     * @param date
     * @param hour
     * @return
     */
    public static Date addHourToDate(Date date, int hour) {
        if (date == null) {
            return date;
        }
        return addTimes(Calendar.HOUR, date, hour);
    }

    /**
     * 给指定的时间添加minute分钟
     *
     * @param date
     * @param minute
     * @return
     */
    public static Date addMinuteToDate(Date date, int minute) {
        if (date == null) {
            return date;
        }
        return addTimes(Calendar.MINUTE, date, minute);
    }


    /**
     * 得到当前年份字符串 格式（yyyy）
     */
    public static String getCurrentYear() {
        return String.valueOf(getByScale(Calendar.YEAR));
    }

    /**
     * 得到当前月份字符串 格式（MM）
     */
    public static String getCurrentMonth() {
        return String.valueOf(getByScale(Calendar.MONTH) + 1);
    }

    /**
     * 得到当前天字符串 格式（dd）
     */
    public static String getCurrentDay() {
        return String.valueOf(getByScale(Calendar.DATE));
    }

    /**
     * 得到当前小时字符串 格式（dd）
     */
    public static String getCurrentHour() {
        return String.valueOf(getByScale(Calendar.HOUR_OF_DAY));
    }

    /**
     * 获取当前时间是本周的第几天
     *
     * @return
     */
    public static String getCurrentDayOfWeek() {
        return String.valueOf(getByScale(Calendar.DAY_OF_WEEK));
    }

    /**
     * 获取当时时间是本月的第几天
     *
     * @return
     */
    public static String getCurrentDayOfMonth() {
        return String.valueOf(getByScale(Calendar.DAY_OF_MONTH));
    }

    /**
     * 获取当前时间是本年的第几天
     *
     * @return
     */
    public static String getCurrentDayOfYear() {
        return String.valueOf(getByScale(Calendar.DAY_OF_YEAR));
    }

    /**
     * 获取本月的第一天
     *
     * @return
     */
    public static String getFirstDayOfMonth() {
        Calendar cale = Calendar.getInstance();
        cale.add(Calendar.MONTH, 0);
        cale.set(Calendar.DAY_OF_MONTH, 1);
        synchronized (DAY_FORMATTER) {
            return DAY_FORMATTER.format(cale.getTime());
        }
    }

    /**
     * 获取本月的最后一天
     *
     * @return
     */
    public static String getLastDayOfMonth() {
        Calendar cale = Calendar.getInstance();
        cale.add(Calendar.MONTH, 1);
        cale.set(Calendar.DAY_OF_MONTH, 0);
        synchronized (DAY_FORMATTER) {
            return DAY_FORMATTER.format(cale.getTime());
        }
    }


    /**
     * 获取两个日期之间的天数
     *
     * @param before
     * @param after
     * @return
     */
    public static double getDifferentDays(Date before, Date after) {
        long beforeTime = before.getTime();
        long afterTime = after.getTime();
        return (afterTime - beforeTime) / (double)(1000 * 60 * 60 * 24);
    }


    /**
     * 计算两个日期之间相差的分钟数
     *
     * @param date1
     * @param date2
     * @return
     */
    public static int minutesBetween(Calendar date1, Calendar date2) {
        long time1 = date1.getTimeInMillis();
        long time2 = date2.getTimeInMillis();
        long betweenDays = Math.abs((time1 - time2) / (1000 * 60));
        return Integer.parseInt(String.valueOf(betweenDays));
    }

    /**
     * 计算两个日期之间相差的天数
     *
     * @param smdate 较小的时间
     * @param bdate  较大的时间
     * @return 相差天数
     * @throws ParseException
     */
    public static int daysBetween(Date smdate, Date bdate) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        smdate = sdf.parse(sdf.format(smdate));
        bdate = sdf.parse(sdf.format(bdate));
        Calendar cal = Calendar.getInstance();
        cal.setTime(smdate);
        long time1 = cal.getTimeInMillis();
        cal.setTime(bdate);
        long time2 = cal.getTimeInMillis();
        long betweenDays = (time2 - time1) / (1000 * 3600 * 24);

        return Integer.parseInt(String.valueOf(betweenDays));
    }


    /**
     * 给指定的时间添加second秒
     *
     * @param date
     * @param second
     * @return
     */
    public static Date addSecondToDate(Date date, int second) {
        if (date == null) {
            return date;
        }
        return addTimes(Calendar.SECOND, date, second);
    }


    /**
     * 给时间加上或减去 指定时间（日时秒等等）
     *
     * @param field  单位（the calendar field），候选值：
     *               <p>Calendar.SECOND：秒；
     *               <p>Calendar.HOUR：时；
     *               <p>Calendar.DATE：天；
     *               <p>等等
     * @param date   添加前的时间
     * @param amount 需要增加的数量，正数为取指定时间后，负数代表指定时间前
     * @return 改变后的时间
     */
    public static Date addTimes(int field, Date date, int amount) {
        if (date == null) {
            return null;
        }
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.add(field, amount);
        date = cal.getTime();
        return date;
    }

    /**
     * 尝试将字符串转换为时间格式
     *
     * @param dateStr
     * @return
     */
    public static Date attemptStringToDate(String dateStr) {

        if (dateStr.contains("T")) {
            return stringToDate(dateStr.substring(0, 10) + " " + dateStr.substring(11, 19), "yyyy-MM-dd HH:mm:ss");
        }

        try {
            return attempStringToDate(dateStr, "yyyy-MM-dd HH:mm:ss");
        } catch (Exception secondException) {
            try {
                return attempStringToDate(dateStr, "yyyy-MM-dd HH:mm");
            } catch (Exception minuteExcption) {
                try {
                    return attempStringToDate(dateStr, "yyyy-MM-dd HH");
                } catch (Exception hourExcption) {
                    try {
                        return attempStringToDate(dateStr, "yyyy-MM-dd");
                    } catch (Exception dayExcption) {
                        return null;
                    }
                }
            }
        }
    }

    private static Date attempStringToDate(String str, String formatterStr) throws Exception {
        SimpleDateFormat format = new SimpleDateFormat(formatterStr);
        return format.parse(str);
    }


    /**
     * 根据传入的规格，返回当前时间的规格时间，如返回当前的年，月，日等
     *
     * @param scale
     * @return
     */
    private static int getByScale(int scale) {
        Calendar cale = Calendar.getInstance();
        return cale.get(scale);
    }

    /**
     * 是否是今天24h内
     * @author LiQun
     * @date 2019/2/19
     * @param inputDate 输入的日期
     * @return flag 是否为今天24小时内
     */
    public static boolean isToday(Date inputDate) throws Exception {
        boolean flag = false;
        // 获取当前系统时间
        long currentTimeMillis = System.currentTimeMillis();
        Date nowDate = new Date(currentTimeMillis);
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DAY);
        String formatDate = simpleDateFormat.format(nowDate);
        // 定义24小时范围
        String beginTime = formatDate + " 00:00:00";
        String endTime = formatDate + " 23:59:59";
        simpleDateFormat = new SimpleDateFormat(DATE_TIME);
        Date parseBeginTime = simpleDateFormat.parse(beginTime);
        Date parseEndTime = simpleDateFormat.parse(endTime);
        // 如果输入时间在24小时范围内,则返回true
        if (inputDate.after(parseBeginTime) && inputDate.before(parseEndTime)) {
            flag = true;
        }
        return flag;
    }
}
